MacsBug results
Volume Number: 7
Issue Number: 9
Column Tag: Developer's Forum
Getting Results With Macsbug 
By Jeff Turnbull, Livingston Manor, NY
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Getting Results with Macsbug: A Checklist of Techniques
[Jeff Turnbull is an independent systems consultant and programmer. He has
worked on OCR, XCMD’s, text patterns, and data acquisition. A graduate of Maharishi
International University, he practices the Transcendental Mediation (TM) technique,
which he considers a tremendous aid to good programming. He resides in the Catskill
mountain region in Livingston Manor, N.Y. He can be reached at (914) 439-4310
(afternoons).]
This article presents several checklists of Macsbug commands and techniques to
get fast results in any language. Anyone can use Macsbug, so long as they have such a
resource. The intent of the article is to give a list of practical techniques to try, without
a lot of extra theory. With these checklists you can usually accomplish the majority of
your debugging with only a small amount of effort. The checklists are designed for
Macsbug, but can be used with other debuggers. The article assumes you have a working
knowledge of the Mac and a little familiarity with debugging. The article has an
introduction, sections containing the checklists of techniques, and some useful
reference information. Also, Macsbug allows certain types of advanced debugging, but
most programmers do not want to have to go through extensive tutorials -- they just
want to know the techniques they can try to get results. Having a reference of techniques
to try for various situations will not only save time, it will produce valuable results
where they could not otherwise be obtained.
One of the main needs is to find out where your program is when it crashes, and a
checklist is presented for this purpose. There are additional checklists on general
techniques, stepping and breakpoints, going past an error, heap and memory,
displaying messages, variable inspection, objects, miscellaneous tips, and hints on
disassembly of your code. The reference information contains sections on invoking
Macsbug, how to turn on symbol names, utility routines for using Macsbug from your
source code, a list of Macsbug commands, and a list of system errors. The article is
arranged so you can easily refer back to it when using Macsbug, to look up commands
and techniques. Within each section the techniques are arranged from the simplest
commands (which are often the most powerful) to more advanced techniques which are
needed less often.
Macsbug is an Apple product available on the Developers CD, from APDA, and as
part of MPW and some other development systems. Macsbug has certain advantages
compared with other debuggers such as SADE. Macsbug is fast, uses little memory,
shows the screen at the time of the crash, no special compiling is necessary, you can log
messages to printer or file as you go, and Macsbug provides several powerful low-level
commands. You can try these checklists of techniques before having to deal with other
debuggers.
The Low-Level Mac
This paragraph describes a few of the ‘low-level’ basics you need to know to help
interpret the information in Macsbug. When a routine executes, space is reserved on
the ‘Stack,’ in first-in-last-out order (or last-in-first-out order). The more
routines which are running, the larger the stack grows. Each routine is said to have a
‘stack frame’ for its storage of local variables, parameters, etc. The stack starts out in
(fairly) high memory and grows downward. The ‘heap’ is another area of memory
where storage for pointers and handles is allocated. Much debugging on the Mac concerns
the heap and moving objects, and you are well advised to become very familiar with its
theory and operation. See the references at the end of the article and the chapters in
Inside Macintosh for more information on the heap. The 68000 microprocessor has
eight ‘address registers’ (internal memory) which hold pointers, and are denoted A0 -
A7. Several of these registers are important. Register A5 usually holds a pointer to the
start of the global variables. Register A6 usually points to the current stack frame.
Register A7 (also called SP or stack pointer) holds a pointer to the bottom of the stack.
However, many of the ROM routines use these registers for other purposes and restore
their correct values only at the end of the routine. The 68000 also has eight data
registers. There are some hints on getting some basic information from disassembly of
code in the section on Just Enough Assembly.
Finding Where an Exception Has Occurred
Your program crashes and you wind up in Macsbug. Here are some techniques you
should try to find out where the program crashed, and gather other useful information.
I suggest you always run through these in order, even if you don’t understand them all.
Later you can look back at the file produced.
• Check the screen by hitting the esc or tilde (~) key, for indications of what the
program was doing when it crashed. Note everything which is on the screen: it
may help as you look over your source code.
• When entering Macsbug check the bottom line for the location of the program
counter.
• Now start by logging everything to a file: LOG Filename or LOG printer {for
ImageWriter}.
• Repeat initial message: HOW.
• Find out where you are: WH. If no name is given, the routine may be a method
call, library routine, or glue for a Mac toolbox call, and there are techniques to
find out the name below.
• Stack crawl: show the calling chain of routines on the stack. First try SC6, which
shows the routines which are currently executing. This command lists the
addresses of the stack frames of the calling chain and the caller’s address. Note
that the last stack frame is not listed: see the next command. (If you are executing
a ROM routine A6 may not point to a stack frame.)
• Finish the stack crawl. There is one more routine on the stack which is not
shown by the stack crawl command (for some reason known only to Apple). The
routine name is usually given by the WH command (above) and the stack frame by
A6. It is good to see what was called by doing ID lastCaller (get lastCaller from the
stack crawl as above). This will disassemble the code of the last routine shown by
the stack crawl command, and will usually give you the name of the final routine.
(See the section on disassembly for more information.) This technique can be done
on any of the routines on the stack. There are other techniques below if no name is
given.
• If the stack crawl using SC6 does not work (due to being in a ROM routine or
other problem) then you can use SC7, and get a frame address to try SC6 again.
SC7 will show more routines, some of which are not really on the stack (they are
left over from previous calls). Then use the lowest valid Frame Addr with the SC6
command to get the calling chain, as in SC6 FrameAddr. This is only necessary if
SC6 does not work, and it may not produce valid results.
• Look at the code which was running when you crashed. IP PC. (The first
instruction or two shown may not be valid until Macsbug gets in sync). If you
need to, then disassemble closer to the start of the routine or go further. Try IP
PC-100 (or more than 100 if necessary, then keep hitting return. Look for calls
to (ROM) routines near the crash and other indicators of where the program was
inside the routine. Look at the hints on assembly language for more information.
• Check the heap. HT. See the section on memory if there is a problem.
• Check for a memory error. DW memErr. It should give zero.
• Check for a resource error. DW resErr. It should give zero.
• You can sometimes get past your error by using the techniques described in the
section on getting past an error.
• Check the parameters to the routine. If you suspect bad parameters, then see the
section on inspecting parameters and variables. Most ROM routines crash because
of the parameters passed to them.
• If no name is given. The routine may be an aborted method call, an unnamed
library routine (such as a Pascal string function), in glue code for a ROM
routine, an unloaded segment, a bad INIT may have taken over, or the PC may have
become invalid. None of these have names. You can try the following techniques,
but if it proves difficult it may become easier to see the section on stepping and
the section on putting messages into your source. First try to reconstruct the
stack calling chain using the techniques above. There may be trouble. Check your
source and compare it to the calling chain (do you have any new code or routines
you suspect?). The problem is not always the last routine on the stack, and it may
be enough to know that you crashed after calling a dialog or other ROM routine.
Disassemble the caller (as above in the point on finish the stack crawl) and see
the section on disassembly for other techniques. Use the WH command on any code
address (e specially the caller from the stack calling chain, and any JSR
statements you find in disassembling code) which will at least show you the heap
and code segment. Glue code, and library routines are usually linked in the main
code segment. Method jumps are often in there own segment (%_SelProcs in MPW
Pascal). INITs and certain Inside Macintosh routines are often in the system heap.
Method calls are usually disassembled as JSR in CODE %_SelProcs. You can
disassemble the addresses of the JSR (and JMP and BSR) statements and see if it
is glue code (there is usually an Atrap fairly soon in the code), or if you hit other
JSR and/or JMP statements then you are probably going through a jump table.
• What to try next. If the error indicates a specific problem, such as a memory
problem, then see the section of this article dealing with the problem. You can try
the list of general techniques. See the section on inspecting variables to inspect
the parameters passed into the routine which crashed. There are many other
techniques you can use to narrow down the problem before recompiling. You can
rerun the program using EA {exit to application} and use the techniques in the
sections below. These techniques will help you step through the error, record
other information such as the toolbox routines called before the crash, and check
the heap before calls. Sometimes it is helpful just to know if the problem repeats
in the same way each time. (If it doesn’t then you may have a bad pointer doing
damage to memory somewhere.) See the section on putting messages into your
source code.
• Turn off logging to file if you are finished debugging: LOG.
• Exit Macsbug. The system may have been corrupted so the safest way to leave
Macsbug is with RB {reboot} or RS {restart}. You can also try EA {exit to
application} or ES {exit to shell}. Also see the techniques for going on past the
error.
General Techniques
Here is a list of general purpose techniques to try out.
• Scramble the heap: HS. All programs should do this at least once to check for
problems with moving handles. Use the command, then run your menu commands.
• If you crash in a ROM routine, check the parameters to the routine. See the
section on inspecting parameters.
• Catch use of NIL pointers: SL 0 ‘NIL!’. This is a good defensive technique to try.
After doing this command Macsbug will be invoked if your program attempts to
dereference a pointer which is NIL (or zero). This command puts ‘NIL!’ (or
$4E494C21, an odd large value) into memory at location zero. An address or bus
error will result if this is dereferenced. This technique doesn’t slow you down one
bit. You can also set memory location zero from your source code.
• Record all the calls to toolbox routines. ATRA. This is very useful when running
code for the first time, or at any time you think the code may crash. You will have
a record of recent calls to toolbox routines. You can play back the last calls using
ATPA. Note that most of the ATrap commands take an “A” on the end to indicate that
the command should only apply to ATraps which are called from the application
(not from Atraps which are called from ROM). If you wish to record all calls,
even calls made by toolbox routines to other toolbox routines, then use ATR and
ATP.
• Check the heap before every toolbox call: ATHCA. Macsbug will break if the heap
is bad. ATHC (without the “A” on the end) may incorrectly inform you that the
heap is bad during calls made by memory manager routines.
• Check a portion of memory at every toolbox call. First go into Macsbug and do a
checksum of a memory range: CS address address+offset. Then do an ATrap
checksum command: ATSSA. Macsbug will break if the memory changes. This
technique is useful to see when a chunk of memory is getting used or
inadvertently stepped on. This can help you find bad pointers or other wrong use
of a variable or memory. If you know that data is going bad, but don’t know why or
when, then use this technique. Then use the techniques above to find out where the
program was when the change occurred.
• Put the Mac into slow motion: SS RomBase^ (RomBase^+40). This will preform
a checksum on the given memory range before each 68000 instruction, which
will really slow things down. You can check a longer or shorter range of memory
to the extent you want by using SS RomBase^ (RomBase^+offset). RomBase is
memory that will not change. You can also use SS memErr memErr+2. This will
preform a checksum on the global variable memErr, which also checks for
memory errors. You can check for resource errors by using SS resErr resErr+2.
These techniques will really slow things down so you can see what happens on the
screen in slow motion.
The above techniques can be used before you recompile, as can some of the
techniques in the next few sections.
Stepping, Going and Breakpoints
This section describes some techniques you can try before checking your source
code and recompiling, and also some things to try if other techniques don’t work. If you
crash, you can rerun the program using EA and try some of these techniques without
having to recompile. First, some common stepping commands to use over and over.
• To go back to your program: G.
• To step one assembly language instruction: S.
• To step several assembly language instructions. S 20 or S numberOfSteps. The S
command will step into any called routines.
• To step over routines by letting them run. SO or SO numberOfSteps. This will
run any called routines at full speed as one step.
• To let a routine finish running at full speed (when you are in the routine, to get
done with it) do a magic return: MR A6. (When doing this command on a ROM
routine, it is best to do this command toward the beginning of the routine, because
some ROM routines use A6 for other purposes and the command may not work.)
• To go till a certain point: GT Address. This is the equivalent of setting a
breakpoint and using the go command. Use this command if you are getting bored
with small steps and you want to get to a certain point, or you just want to get past
where you are now. You can use routine names in place of “Address.” Also see the
point below on breakpoints. The above commands are the basic commands for
stepping and going.
• Using breakpoints in your code. BR Address. The easiest place to break is at the
start of a routine by using its name: BR RoutineName. If you set a breakpoint in
the middle of a routine you should first disassemble your code (IP command) and
start on an instruction boundary, or else an error may occur, as in BR
RoutineName+offset. Hint: to set a breakpoint at a later point in a routine you can
use the colon, which tells Macsbug to use the current name: BR :+offset.
• Using breakpoints with commands: BR Address ;HC;G. This command will check
the heap (HC) at the break and then go on (G). You can use any Macsbug commands
(preceded by semicolons), such as TD (total display of registers), SC6 (stack
crawl), IP (to disassemble the current code), DM Address (display memory), CS
(checksum), etc.
• Using Atrap breaks: ATBA. This will break into Macsbug on all ATraps called
from your program. You will often want to narrow it down, for example: ATBA
SetPort.
• Finding strange behavior using Atraps. If your program is not behaving as it
should, and you think that ROM routines are getting called (when they shouldn’t
be getting called), then set breakpoints on the suspected ROM routines. ATB
RoutineName. Then follow the instructions for finding out where the program is.